package com.itlong.cloud.controller.miniprogram;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.itlong.cloud.POJO.PO.UserLoginLogPO;
import com.itlong.cloud.POJO.VO.MessageVO;
import com.itlong.cloud.POJO.VO.app.AppUserCacheVO;
import com.itlong.cloud.POJO.VO.app.AppUserOwnProjectVO;
import com.itlong.cloud.POJO.VO.miniprogram.MiniProgramLoginVO;
import com.itlong.cloud.PlatformConstants;
import com.itlong.cloud.abstracts.BaseController;
import com.itlong.cloud.annotation.LoggerInfo;
import com.itlong.cloud.annotation.UnAuthAccessRequired;
import com.itlong.cloud.annotation.UnParamEncrypt;
import com.itlong.cloud.constants.LogDescConstant;
import com.itlong.cloud.constants.RedisConstant;
import com.itlong.cloud.enumerate.SystemTypeEnum;
import com.itlong.cloud.enumerate.ecode.BaseErrorCodeEnum;
import com.itlong.cloud.enumerate.ecode.MiniProgramErrorCodeEnum;
import com.itlong.cloud.service.RedisService;
import com.itlong.cloud.thrown.DataAccessException;
import com.itlong.cloud.user.app.IAppMineServiceClient;
import com.itlong.cloud.user.app.IAppRegisterServiceClient;
import com.itlong.cloud.user.miniprogram.IMiniProgramLoginServiceClient;
import com.itlong.cloud.utils.encrypt.Md5EncryptUtil;
import com.itlong.cloud.utils.encrypt.Sha1EncryptUtil;
import com.itlong.cloud.utils.network.http.HttpProtocolUtil;
import com.itlong.cloud.utils.words.StringHandlerUtil;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.security.spec.AlgorithmParameterSpec;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <desc>
 *     小程序登录与注册版块控制器
 * </desc>
 *
 * @create  2018/11/19
 */
@RestController
@RequestMapping("/user")
@RefreshScope
public class MiniprogramRegisterController extends BaseController{

    private static final Logger LOG = LoggerFactory.getLogger(MiniprogramRegisterController.class);

    @Value("${" + PlatformConstants.MINI_PROGRAM_REQUEST_PARAMS_ENCRYPT_KEY + "}")
    public String MINI_PROGRAM_REQUEST_PARAMS_ENCRYPT_KEY;

    @Value("${" + PlatformConstants.MINI_PROGRAM_USER_TOKEN_KEY + "}")
    public String MINI_PROGRAM_USER_TOKEN_KEY;

    @Value("${" + PlatformConstants.MINI_PROGRAM_REQUEST_URL + "}")
    public String MINI_PROGRAM_REQUEST_URL;

    @Value("${" + PlatformConstants.MINI_PROGRAM_APPID + "}")
    public String MINI_PROGRAM_APPID;

    @Value("${" + PlatformConstants.MINI_PROGRAM_SECRET + "}")
    public String MINI_PROGRAM_SECRET;

    @Value("${" + PlatformConstants.WECHAT_OFFICIAL_APPID + "}")
    public String WECHAT_OFFICIAL_APPID;

    @Value("${" + PlatformConstants.WECHAT_OFFICIAL_SECRET + "}")
    public String WECHAT_OFFICIAL_SECRET;

    @Value("${" + PlatformConstants.WECHAT_OFFICIAL_REQUEST_URL + "}")
    public String WECHAT_OFFICIAL_REQUEST_URL;

    @Autowired
    IMiniProgramLoginServiceClient iMiniProgramLoginServiceClient;

    @Autowired
    IAppRegisterServiceClient iAppRegisterServiceClient;

    @Autowired
    IAppMineServiceClient iAppMineServiceClient;

    @Autowired
    private RedisService<AppUserCacheVO> redisService;



    /**
     *  <desc>
     *      解密与微信绑定的手机号
     *  </desc>
     *
     * @param encrypdata 微信参数
     * @param ivdata 微信参数
     * @param sessionKey 会话密钥
     * @return
     * @createDate 2018/11/24
     * @author Juguang.S
     */
    @RequestMapping(path = "/getPhoneNum",method = RequestMethod.POST)
    @ResponseBody
    @UnParamEncrypt
    public Object getPhoneNum(String encrypdata,String ivdata,String sessionKey){
        try {
            if(StringUtils.isEmpty(encrypdata) || StringUtils.isEmpty(ivdata) || StringUtils.isEmpty(sessionKey)){
                return new MessageVO(BaseErrorCodeEnum.NOT_PARAM.getErrorCode());
            }

            byte[] encrypData = Base64.decode(encrypdata);
            byte[] ivData = Base64.decode(ivdata);
            byte[] sessionKeyByte = Base64.decode(sessionKey);
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyByte, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            //解析解密后的字符串 
            String phoneNum = new String(cipher.doFinal(encrypData),"UTF-8");

            return  new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode(),phoneNum);
        }catch (Exception e){
            throw new DataAccessException("【小程序_注册登录版块】解密与微信绑定的手机号失败",e);
        }
    }

    /**
     *  <desc>
     *      获取微信openId sessionKey
     *  </desc>
     *
     * @param code 微信参数
     * @param type 1为微信 2为公众号
     * @return
     * @createDate 2018/12/7
     * @author Juguang.S
     */
    @RequestMapping(path = "/getOpenId",method = RequestMethod.POST)
    @ResponseBody
    @UnParamEncrypt
    public Object getOpenId(String code, String type, String billId) {
//        HttpServletRequest request= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        code=request.getAttribute("code")==null?null:request.getAttribute("code").toString();
//        type=request.getAttribute("type")==null?null:request.getAttribute("type").toString();
        String requestUrl = "";
        Map<String, Object> map = new HashMap<>();
        String status = "1";
        String msg = "ok";
        try {
            if (type.equals("1")) {
                requestUrl = MINI_PROGRAM_REQUEST_URL + "?appid=" + MINI_PROGRAM_APPID + "&secret=" + MINI_PROGRAM_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
            } else if (type.equals("2")) {
                requestUrl = WECHAT_OFFICIAL_REQUEST_URL + "?appid=" + WECHAT_OFFICIAL_APPID + "&secret=" + WECHAT_OFFICIAL_SECRET + "&code=" + code + "&grant_type=authorization_code";
            } else if (type.equals("3")) {
                Map<String, Object> appIdByBillId = iAppMineServiceClient.getAppIdByBillId(billId);
                requestUrl = WECHAT_OFFICIAL_REQUEST_URL + "?appid=" + appIdByBillId.get("appId") + "&secret=" + appIdByBillId.get("appSecret") + "&code=" + code + "&grant_type=authorization_code";
            }

            if(StringUtils.isBlank(code)){
                status = "0";//失败状态
                msg = "code为空";
            }else {
                // 发起GET请求获取凭证
                JSONObject jsonObject = HttpProtocolUtil.httpsRequest(requestUrl, "GET", null);
                if (jsonObject != null) {
                    //LOG.info("【获取openid的返回结果集】"+jsonObject.toJSONString());
                    try {
                        map.put("openid", jsonObject.getString("openid"));
                        map.put("session_key", jsonObject.getString("session_key"));
                        map.put("unionId", jsonObject.getString("unionid"));
                    } catch (JSONException e) {
                        // 获取token失败
                        status = "0";
                        msg = "code无效";
                    }
                }else {
                    status = "0";
                    msg = "code无效";
                }
            }
            map.put("status", status);
            map.put("msg", msg);
        } catch (Exception e) {
            throw new DataAccessException("【小程序】获取openId失败",e);
        }
        return new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode(),map);

    }

    /**
     *  <desc>
     *      授权登录
     *  </desc>
     *
     * @param phoneNo 手机号
     * @return
     * @createDate 2018/11/24
     * @author Juguang.S
     */
    @LoggerInfo(identifer = "login")
    @RequestMapping(path = "/login",method = RequestMethod.POST)
    @ResponseBody
    public Object login(String phoneNo, String createTime, String sign,String openId,String unionId){
        HttpServletRequest request= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        phoneNo=request.getAttribute("phoneNo")==null?null:request.getAttribute("phoneNo").toString();
        createTime=request.getAttribute("createTime")==null?null:request.getAttribute("createTime").toString();
        sign=request.getAttribute("sign")==null?null:request.getAttribute("sign").toString();
        openId=request.getAttribute("openId")==null?null:request.getAttribute("openId").toString();
        unionId=request.getAttribute("unionId")==null?null:request.getAttribute("unionId").toString();
        try {
            if(StringUtils.isEmpty(phoneNo) || StringUtils.isEmpty(openId)){
                return new MessageVO(BaseErrorCodeEnum.NOT_PARAM.getErrorCode());
            }

            //验签
            Map<String,String> params = new HashMap<>();
            params.put("createTime",createTime);
            params.put("phoneNo",phoneNo);
            params.put("openId",openId);
            params.put("unionId",unionId);
            //验证签名
            if(!Md5EncryptUtil.checkAppSignValue(params,sign,MINI_PROGRAM_REQUEST_PARAMS_ENCRYPT_KEY)){
                return new MessageVO(MiniProgramErrorCodeEnum.MINI_PROGRAM_SIGN_ERR.getErrorCode());
            }

            MessageVO messageVO = null;
            //校验该用户是否被禁用
            if(iMiniProgramLoginServiceClient.getUserStatus(phoneNo)>0){
                return new MessageVO(MiniProgramErrorCodeEnum.USER_ACCOUNT_REFUSE.getErrorCode());
            }
            AppUserCacheVO appUserCacheVO = iMiniProgramLoginServiceClient.login(phoneNo,openId,unionId);

            List<AppUserOwnProjectVO> userOwnProjectList = iAppRegisterServiceClient.getUserOwnProjectsInfo(appUserCacheVO.getUserId(),"-1");
            if (userOwnProjectList != null&&userOwnProjectList.size()>0){ //未申请房屋认证审核通过或者全部房间均被迁出
                if (StringUtils.isBlank(appUserCacheVO.getCurrentProjectId())||StringUtils.isBlank(appUserCacheVO.getCurrentProjectName())){
                    appUserCacheVO.setCurrentProjectId(userOwnProjectList.get(0).getProjectId());
                    appUserCacheVO.setCurrentProjectName(userOwnProjectList.get(0).getProjectName());
                }
            }else {
                appUserCacheVO.setCurrentProjectId("");
                appUserCacheVO.setCurrentProjectName("");
            }
            appUserCacheVO.setProjects(userOwnProjectList);
            boolean redisflag = iMiniProgramLoginServiceClient.loginCacheUserVO(appUserCacheVO);
            if (redisflag){
                appUserCacheVO = (AppUserCacheVO) StringHandlerUtil.formatString(appUserCacheVO);
            }else {
                LOG.info("【小程序_注册登录版块_登录缓存用户失败】");
                throw new DataAccessException("【小程序_登录缓存用户】登录失败");
            }

            MiniProgramLoginVO miniProgramLoginVO = new MiniProgramLoginVO();
            miniProgramLoginVO.setToken(appUserCacheVO.getMiniToken());
            miniProgramLoginVO.setPhotoUrl(appUserCacheVO.getPhotoUrl());
            miniProgramLoginVO.setPhoneNo(phoneNo);
            miniProgramLoginVO.setNickName(appUserCacheVO.getNickName());

            //处理小程序登录日志所需信息
            UserLoginLogPO userLoginLogPO = new UserLoginLogPO(phoneNo,null, HttpProtocolUtil.getIpAddr(request));
            userLoginLogPO.setSystemType(SystemTypeEnum.MINI_PROGRAM.getType());
            userLoginLogPO.setRealName(appUserCacheVO.getUserName());
            userLoginLogPO.setDescription("登录" + BaseErrorCodeEnum.SUCCESS.getErrorCode().getCodeMsg());
            request.setAttribute("loginUser",userLoginLogPO);

            return new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode(),miniProgramLoginVO);
        }catch (Exception e){
            throw new DataAccessException("【小程序_注册登录版块】授权登录失败",e);
        }
    }

    /**
     *  <desc>
     *      退出登录
     *  </desc>
     *
     * @param token
     * @return
     * @createDate 2018/11/24
     * @author Juguang.S
     */
    @LoggerInfo(operateDesc = LogDescConstant.LOGIN_OUT)
    @RequestMapping(path = "/loginOut",method = RequestMethod.POST)
    @ResponseBody
    public Object loginOut(String token, String createTime, String sign){
        try {
            HttpServletRequest request= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            token=request.getAttribute("token")==null?null:request.getAttribute("token").toString();
            createTime=request.getAttribute("createTime")==null?null:request.getAttribute("createTime").toString();
            sign=request.getAttribute("sign")==null?null:request.getAttribute("sign").toString();
            //验签
            Map<String,String> params = new HashMap<>();
            params.put("createTime",createTime);
            //验证签名
            if(!Md5EncryptUtil.checkAppSignValue(params,sign,MINI_PROGRAM_REQUEST_PARAMS_ENCRYPT_KEY)){
                return new MessageVO(MiniProgramErrorCodeEnum.MINI_PROGRAM_SIGN_ERR.getErrorCode());
            }

            if(StringUtils.isEmpty(token)){
                return new MessageVO(BaseErrorCodeEnum.NOT_PARAM.getErrorCode());
            }
            iMiniProgramLoginServiceClient.loginOut(token);
            //LOG.info("【小程序_注册登录版块】登出清理缓存成功");
            return new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode());
        }catch (Exception e){
            throw new DataAccessException("【小程序_注册登录版块】退出登录失败",e);
        }
    }

    /**
     *  <desc>
     *      获取微信授权信息
     *  </desc>
     *
     * @return
     * @createDate 2019/04/03
     * @author Juguang.S
     */
    @RequestMapping(path = "/getTicket",method = RequestMethod.POST)
    @ResponseBody
    @UnAuthAccessRequired
    @UnParamEncrypt
    public Object getJsapiTicket(String url, String billId) throws Exception {
        //url = "http://test.wx.jia-r.com";
//        HttpServletRequest request= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        url=request.getAttribute("url")==null?null:request.getAttribute("url").toString();
        url = URLDecoder.decode(url, "UTF-8");
        Map<String, String> map = new HashMap<>();
        Map<String, Object> data = new HashMap<>();
        if (StringUtils.isNotBlank(billId)) {
            data = iAppMineServiceClient.getAppIdByBillId(billId);
        }
        // true 为有完整的公众号授权信息
        boolean flagAppIdSecret = data.get("appId") != null && StringUtils.isNotBlank(data.get("appId").toString())
                && data.get("appSecret") != null && StringUtils.isNotBlank(data.get("appSecret").toString());
        // true 为开启物业增值
        boolean flagPropertyValue = data.get("propertyValueOpen") != null && Integer.parseInt(data.get("propertyValueOpen").toString()) == 1;
        // 未开启物业增值同时也没有完整的公众号授权信息时，同时兼容账单id未传情况（兑换优惠券时）
        if (!flagPropertyValue && !flagAppIdSecret && StringUtils.isNotBlank(billId)) {
            // 有appId但是没有appSecret时，优化返回提示
            if ((data.get("appId") != null && StringUtils.isNotBlank(data.get("appId").toString()))
                    && (data.get("appSecret") == null || StringUtils.isBlank(data.get("appSecret").toString()))) {
                map.put("hasAppSecret", "nope");
            }
            map.put("propertyValueOpen", data.get("propertyValueOpen") == null ? "" : data.get("propertyValueOpen").toString());
            return new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode(), map);
        }
        String requestUrl = "https://api.weixin.qq.com/cgi-bin/token" + "?appid=" + ((flagPropertyValue || StringUtils.isBlank(billId)) ? WECHAT_OFFICIAL_APPID : data.get("appId")) +
                "&secret=" + ((flagPropertyValue || StringUtils.isBlank(billId)) ? WECHAT_OFFICIAL_SECRET : data.get("appSecret")) + "&grant_type=client_credential";
        try {
            //先从缓存中取access_token和ticket其有效期为两个小时，如取不到再调接口获取
            String ticket = redisService.get(((flagPropertyValue || StringUtils.isBlank(billId)) ? RedisConstant.WECHAT_OFFICIAL_TICKET : (RedisConstant.WECHAT_OFFICIAL_TICKET + data.get("appId"))));
            //url = "http://develop.wx.jia-r.com/?code=0018eRlg2PT5xI0oGfmg2DZElg28eRlZ&state=";
            if (StringUtils.isEmpty(ticket)) {
                //发起GET请求获取凭证
                JSONObject jsonObject = HttpProtocolUtil.httpsRequest(requestUrl, "GET", null);
                if (jsonObject != null) {
                    String accessToken = jsonObject.getString("access_token");
                    if (StringUtils.isNotEmpty(accessToken)) {
                        redisService.set(((flagPropertyValue || StringUtils.isBlank(billId)) ? RedisConstant.WECHAT_OFFICIAL_ACCESS_TOKEN : (RedisConstant.WECHAT_OFFICIAL_ACCESS_TOKEN + data.get("appId"))),
                                RedisConstant.WECHAT_OFFICIAL_TIME, accessToken);
                    }
                    //LOG.info("【【微信公众号】获取ticket】access_token:" + accessToken);
                    String requestSecondUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket" + "?access_token=" + accessToken + "&type=JSAPI";
                    JSONObject jsonObjectSecond = HttpProtocolUtil.httpsRequest(requestSecondUrl, "GET", null);
                    if (jsonObjectSecond != null) {
                        ticket = jsonObjectSecond.getString("ticket");
                        if (StringUtils.isNotEmpty(ticket)) {
                            redisService.set(((flagPropertyValue || StringUtils.isBlank(billId)) ? RedisConstant.WECHAT_OFFICIAL_TICKET : (RedisConstant.WECHAT_OFFICIAL_TICKET + data.get("appId"))),
                                    RedisConstant.WECHAT_OFFICIAL_TIME, ticket);
                        }
                    }
                }
            }
            map = Sha1EncryptUtil.getSign(ticket, url);
            map.put("appId", data.get("appId") == null ? "" : data.get("appId").toString());
            map.put("propertyValueOpen", data.get("propertyValueOpen") == null ? "" : data.get("propertyValueOpen").toString());
            //LOG.info("【【微信公众号】获取ticket】生成签名参数信息:"+map.toString());
        } catch (Exception e) {
            throw new DataAccessException("【微信公众号】获取ticket失败",e);
        }
        return new MessageVO(BaseErrorCodeEnum.SUCCESS.getErrorCode(),map);
    }
}
